一个Spring Boot应用容器化及Kubernetes部署全记录。
Docker & Kubernetes
Docker是一种容器技术,它能将应用所需的所有内容打包成一个Docker镜像,其目标是使开发者能方便地创建、部署、运行应用。Docker很像虚拟技术,其依赖的基础镜像通常是超精简版的OS镜像,但最关键的一点就是能保证在不同的宿主机环境下容器运行效果的一致性。
Kubernetes则是一种容器编排平台,且不仅仅支持Docker容器,当然主流的用户还是使用Docker容器。Kubernetes的强大之处在于它能支持大量容器应用协同运行的同时减少运维负担,如能根据硬件资源的状况在宿主机集群上调度容器、根据用户需求自动快速扩缩容等。
项目
这是一个真实的项目,涵盖了大部分常见的Spring Boot使用方式。
- 出于脱敏的目的,项目名以Test替代。
项目的结构如下:
1
2
3
4
5
6
7
8
9
10
11
12
13Test
- src
- main
- java
- resources
- test
- java
- target
- .gitignore
- application.yml
- Dockerfile
- pom.xml
- README.md项目根据环境不同需要读取不同的
application.yml
。- 项目使用logback作为日志工具,需要读取一个
logback-spring.xml
的配置文件。 - 项目还需要读取一个properties文件。
- 为方便期间,所有Kubernetes资源均在default namespace下。
namespace
Kubernetes的许多资源对象都隶属于某个namespace,namespace是对资源的逻辑划分,不同namespace下的资源可以做到简单隔离。
准备工作
镜像
针对项目jar包,我们需要将其打成一个Docker镜像,而Dockerfile
就是打包过程的指导文件,类似于make
过程中的Makefile
。
jar->Docker镜像
以下是我们需要的Dockerfile内容。1
vim Dockerfile
1 | FROM openjdk:8-jdk-alpine |
FROM
表示要生产的镜像的基础镜像,我们使用jdk8。VOLUME
表示定义一个匿名卷,也可以理解为创建一个目录,这里的/tmp
是由于tomcat的需要。ADD
表示将项目编译后的jar包拷贝到镜像里,默认为根目录/
。ENTRYPOINT
表示入口点,即容器启动时执行的命令,通过上述组合,实际执行的命令为:java -Djava.security.egd=file:/dev/./urandom -jar -Dspring.config.location=/etc/config/application.yml -Dlogging.config=/etc/config/logback-spring.xml -DconfPath=/etc/config /test.jar
注意这里的配置路径/etc/config
还不存在,下文中会创建和用到。
配置
Kubernetes ConfigMap
ConfigMap可以用来保存单个的键值对,也可以保存配置文件。
配置文件->ConfigMap
针对项目配置,需要做的改造就是把配置文件(应用、日志、业务)转化成ConfigMap对象,从而让Kubernetes化后的应用能够读取。1
vim test-config.yml
1 | apiVersion: v1 |
- 注意配置文件按照yaml规范缩进。
data
属性里记录实际的配置,本质上仍是键值对,如第一个数据的key为application-yml,内容为其配置内容。
应用部署定义
Kubernetes Deployment
Deployment是一种控制容器组的对象,在Deployment中定义一个期望的容器组的数量,Deployment在创建后会维持这个数量,当数量少于期望时会新建容器组,反之会停止超额的容器组。
部署->Deployment
针对本项目,对应的Deployment配置如下。1
vim test-deployment.yml
1 | apiVersion: extensions/v1beta1 |
replicas
即为此Deployment期望的容器组的数量。metadata
.labels
用于跟下文中的Service对接。spec
.selector
.matchLabels
与容器组的spec
.template
.metadata
.labels
对接。spec
.template
.spec
.volumes
定义了一些卷,name
为卷名。- 如果定义了
configMap
属性,则items
属性的key
对应上文中ConfigMap对象的名称,path
则为要输出的文件,如第一个item的行为是读取ConfigMap对象test-config
的第一个数据的内容(key=application-yml),并将其保存为application.yml
配置文件。 - 如果定义了
hostPath
属性,则是宿主机上的同名目录,如此处将容器日志输出到宿主机的/var/log/test
路路径。
- 如果定义了
spec
.template
.spec
.containers
定义了容器组中的容器,包括容器名、镜像、容器暴露的端口,以及要挂载的卷,在volumeMounts
中,name
为上述卷名,mountPath
为挂载路径,从而容器内部存在/etc/config
路径,且可以读取到上述配置文件。
服务定义
Kubernetes Service
默认情况下,Kubernetes集群内的容器是不能被集群外访问的。此时我们需要将应用连接到Service,Service可以对集群内部暴露,也可以对外暴露宿主机端口,还能通过LB VIP暴露服务。
外部访问->Service
针对本项目,对应的Service配置如下。1
vim test-service.yml
1 | apiVersion: v1 |
spec
.selector
用于连接容器组,与上文中Deployment定义的容器组的labels
对应。spec
.type
定义Service的暴露方式,NodePort
为对集群外暴露,用户通过宿主机端口访问应用,即:- 用户->宿主机IP:30777->Service:7777->容器:7777
部署工作
编译jar包
不论使用Maven还是Gradle,生成jar包,此处不细说。
生成Docker镜像
使用以下命令生成Docker镜像,当然,前提是生成镜像的机器上需要有基础镜像openjdk:8-jdk-alpine
。1
2cd Test
docker build -t test:latest .
同时,需要让所有Kubernetes的Node能获取到该镜像,有两种途径:
将该镜像打包并在所有Node上加载。
1
2docker save -o test.tar test:latest
docker load -i test.tar将该镜像推送到所有Node均能访问的镜像仓库里。
创建Kubernetes资源
分别创建ConfigMap、Deployment、Service。1
2
3kubectl apply -f test-config.yml
kubectl apply -f test-deployment.yml
kubectl apply -f test-service.yml
验证
查看资源对象是否正常。1
2
3kubectl get po | grep test
kubectl get deploy | grep test
kubectl get svc | grep test
访问服务。1
curl <宿主机IP>:30777